package org.lgiavedoni.android.franq.service.config;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.lgiavedoni.android.franq.core.bean.ConfigBean;
import org.lgiavedoni.android.franq.core.dao.DaoObject;
import org.lgiavedoni.android.franq.core.service.Service;
import org.lgiavedoni.android.franq.core.sync.imp.GenericDao;
import org.lgiavedoni.android.franq.event.internal.ConfigurationChangeEvent;
import org.lgiavedoni.android.franq.service.event.EventService;
import org.lgiavedoni.android.franq.service.log.Logger;
import org.lgiavedoni.android.franq.util.RandomUtil;
import org.lgiavedoni.android.franq.util.date.DateUtil;

public class ConfigService extends Service {

	public static final ConfigService inst = new ConfigService();
	private final Map<ConfigKey, ConfigBean> values = new HashMap<ConfigKey, ConfigBean>();
	private final Map<ConfigKey, String> newValues = new HashMap<ConfigKey, String>();
	private final Properties properties = new Properties();

	public static enum ConfigKey {
		JABBER_HOST("209.85.229.125"), //
		JABBER_PORT("5222"), //
		JABBER_SERVICE("gmail.com"), //
		FRANK_USERNAME("lgiavedoni.android@gmail.com"), //
		FRANK_PASSWORD("qwerty22"), //
		GOOGLE_USERID("211105496985521923247"), //
		PLACE_MAX_VALID_DISTANCE("100"), //
		// PLACE_NUM_OF_NECESARY_POSITIONS("2"), //
		PLACE_MIN_STAY_TIME(String.valueOf(DateUtil.getMillisecondsOfMins(5))), //
		// POSITION_UPDATE_PERIOD(String.valueOf(DateUtil.getMillisecondsOfMins(15))),
		// //
		POSITION_UPDATE_PERIOD(String.valueOf(DateUtil.getMillisecondsOfMins(5))), //
		POSITION_UNDEFINED_WAIT(String.valueOf(DateUtil.getMillisecondsOfMins(2))), //
		POSITION_WAITE_BEFORE_CHECK(String.valueOf(DateUtil.getMillisecondsOfMins(4))), //
		POSITION_SAME_NEW_EVENT_SLEEP(String.valueOf(DateUtil.getMillisecondsOfMins(3))), //

		LOG_LEVEL("info"), //
		// QUESTION_TIMEOUT(String.valueOf(DateUtil.getMillisecondsOfMins(2)) +
		// "/" + String.valueOf(DateUtil.getMillisecondsOfMins(3))), //
		QUESTION_TIMEOUT("100/200"), //
		QUESTION_SERVICE_SLEEP(String.valueOf(DateUtil.getMillisecondsOfMins(30)) + "/" + String.valueOf(DateUtil.getMillisecondsOfMins(180))), //
		FRANK_SILENCE("false"), //
		LOCATION_SERVICE_TIME_OUT("60000"), //
		LOCATION_VALID_TIME(String.valueOf(DateUtil.getMillisecondsOfMins(2))), //
		HTTPSERVER_PORT("8080"), //
		SENSOR_LISTENER_DISABLE_PERIOD(String.valueOf(DateUtil.getMillisecondsOfMins(5))),

		// Calendar
		CALENDAR_NAME("Frank"), //

		// Predictions
		PREDICTION_CALENDAR_NAME("Frank_Prediction"), //

		// VoiceRecognition
		VOICE_RECOGNITION("false"), //

		// Google Analytic
		ANALYTIC_CODE("UA-8525771-6"), //

		// User info
		USER_DEFAULT_ACCOUNT("lgiavedoni@gmail.com"), //

		// Service
		SERVICE_CRON_ENABLE(true), //
		SERVICE_LOGGER_ENABLE(true), //
		SERVICE_DB_ENABLE(true), //
		SERVICE_CONFIG_ENABLE(true), //
		SERVICE_EVENTS_ENABLE(true), //
		SERVICE_CHAT_ENABLE(true), //
		SERVICE_LOCATION_ENABLE(true), //
		SERVICE_SYNC_ENABLE(true), //
		SERVICE_PHONELISTENERS_ENABLE(true), //
		SERVICE_MESSAGEINTERCEPTOR_ENABLE(true), //
		SERVICE_HTTPSERVER_ENABLE(true), //
		SERVICE_QUESTIONS_ENABLE(true), //
		SERVICE_PREDICTION_ENABLE(true), //
		SERVICE_CONTACT_ENABLE(true), //
		SERVICE_CALENDAR_ENABLE(true), //
		SERVICE_ANALYTIC_ENABLE(true), //
		SERVICE_COMMUNICATION_ENABLE(true);

		private String value;

		private ConfigKey(String value) {
			this.value = value;
		}

		private ConfigKey(boolean value) {
			this.value = String.valueOf(value);
		}

		public String getDefaultValue() {
			return value;
		}

		public String getName() {
			return name();
		}

		@Override
		public String toString() {
			return name() + " [" + value + "]";
		}
	}

	private ConfigService() {

		// ONLY FOR TESTING
		// setValue(ConfigKey.SENSOR_LISTENER_DISABLE_PERIOD, "10000");
		// setValue(ConfigKey.PLACE_MIN_STAY_TIME, "30000");
	}

	public String getValue(ConfigKey key) {
		return values.get(key) == null ? key.getDefaultValue() : values.get(key).getValue();
	}

	public String getValue(String key) {
		return values.get(ConfigKey.valueOf(key.toUpperCase())).getValue();
	}

	public int getValueAsInt(ConfigKey key) {
		return values.get(key) == null ? Integer.valueOf(key.getDefaultValue()).intValue() : Integer.valueOf(values.get(key).getValue()).intValue();
	}

	public long getRandomValueAsLong(ConfigKey key) {
		String value = values.get(key) == null ? key.getDefaultValue() : values.get(key).getValue();
		long min = Long.valueOf(value.split("/")[0]);
		long max = Long.valueOf(value.split("/")[1]);

		return (long) RandomUtil.valueBetween(min, max);
	}

	public long getValueAsLong(ConfigKey key) {
		return values.get(key) == null ? Long.valueOf(key.getDefaultValue()).longValue() : Integer.valueOf(values.get(key).getValue()).longValue();
	}

	public boolean getValueAsBoolean(ConfigKey key) {
		return values.get(key) == null ? Boolean.valueOf(key.getDefaultValue()) : Boolean.valueOf(values.get(key).getValue());
	}

	public boolean getValueAsBoolean(String key) {
		return getValueAsBoolean(ConfigKey.valueOf(key));
	}

	public void setValue(ConfigKey key, boolean value) {
		newValues.put(key, String.valueOf(value));
	}

	public void setValue(String key, boolean value) {
		newValues.put(ConfigKey.valueOf(key.toUpperCase()), String.valueOf(value));
	}

	public void setValue(ConfigKey key, String value) {
		newValues.put(key, value);
	}

	public void setValue(ConfigKey key, long value) {
		newValues.put(key, String.valueOf(value));
	}

	public void setValue(String key, String value) {
		newValues.put(ConfigKey.valueOf(key.toUpperCase()), value);
	}

	public final Collection<ConfigBean> getValues() {
		return values.values();
	}

	/**
	 * This method update the current values with the new values
	 * 
	 */
	public void updateConfig() {
		Logger.inst.info("ConfigService: updating configuration.");
		Set<ConfigKey> updatedKeys = new HashSet<ConfigKey>();
		for (ConfigKey key : newValues.keySet()) {
			if (!values.get(key).getValue().equals(newValues.get(key))) {
				values.get(key).setValue(newValues.get(key));
				GenericDao.inst.update(values.get(key));
				updatedKeys.add(key);
			}
		}
		EventService.inst.fireEvent(new ConfigurationChangeEvent(updatedKeys));
		newValues.clear();
	}

	@Override
	public String getServiceName() {
		return "config";
	}

	@Override
	protected void _run() {
		// TODO Auto-generated method stub

	}

	@Override
	protected void stopInternal() {
		// TODO Auto-generated method stub

	}

	@Override
	protected void startInternal() throws FileNotFoundException, IOException {
		// Load values from DB
		for (DaoObject cb : GenericDao.inst.getAll(ConfigBean.class)) {
			values.put(((ConfigBean) cb).getKey(), (ConfigBean) cb);
		}

		// Check with the enum
		for (ConfigKey ck : ConfigKey.values()) {
			if (values.get(ck) == null) {
				ConfigBean cb;
				if (properties.containsKey(ck.getName())) {
					cb = new ConfigBean(ck, properties.getProperty(ck.getName()));
				} else {
					cb = new ConfigBean(ck, ck.getDefaultValue());
				}
				values.put(ck, cb);
				GenericDao.inst.add(cb);
			}
		}
	}

	public void addPropertiesSource(InputStream is) {
		try {
			properties.load(is);
		} catch (IOException e) {
			Logger.inst.error(e);
		}
	}

	public void restoreConfig() {
		// boolean update = false;
		for (Object key : properties.keySet()) {
			try {
				// if (!values.containsKey(ConfigKey.valueOf(key.toString()))) {
				setValue(key.toString(), properties.getProperty((String) key));
				// update = true;
				// }
			} catch (Exception e) {
				Logger.inst.error(e);
			}
		}
		// if (update)
		updateConfig();
	}
}